module nade.region;

import nade.vector;


struct TRegion2(T) {
	TVector2!(T) min, max;

	static TRegion2 opCall(const TRegion2!(T) src)
	{
		TRegion2 r;
		r.min = src.min;
		r.max = src.max;
		return r;
	}
	static TRegion2 opCall(const TVector2!(T) min, const TVector2!(T) max)
	{
		TRegion2 r;
		r.min = min;
		r.max = max;
		return r;
	}
	static TRegion2 opCall(T fx, T fy, const TVector2!(T) max)
	{
		TRegion2 r;
		r.min = TVector2!(T)(fx, fy);
		r.max = max;
		return r;
	}
	static TRegion2 opCall(const TVector2!(T) min, T tx, T ty)
	{
		TRegion2 r;
		r.min = min;
		r.max = TVector2!(T)(tx, ty);
		return r;
	}
	static TRegion2 opCall(T fx, T fy, T tx, T ty)
	{
		TRegion2 r;
		r.min = TVector2!(T)(fx, fy);
		r.max = TVector2!(T)(tx, ty);
		return r;
	}

	TRegion2 opAssign(const TRegion2 rhs) { min = rhs.min; max = rhs.max; return this; }

	int opApply(int delegate(ref TVector2!(T)) dg)
	{
		TVector2!(T) coord;
		for(coord.y = min.y;  coord.y < max.y;  ++coord.y)
		{
			for(coord.x = min.x;  coord.x < max.x;  ++coord.x)
			{
				const d = dg(coord);
				if(d != 0) return d;
			}
		}

		return 0;
	}
	int opApplyReverse(int delegate(ref TVector2!(T)) dg)
	{
		TVector2!(T) coord;
		for(coord.y = cast(T)(max.y - 1);  coord.y >= min.y;  --coord.y)
		{
			for(coord.x = cast(T)(max.x - 1);  coord.x >= min.x;  --coord.x)
			{
				const d = dg(coord);
				if(d != 0) return d;
			}
		}

		return 0;
	}

	TRegion2 merge(const TVector2!(T) rhs) const
	{
		return TRegion2(min.min(rhs), max.max(rhs));
	}
	TRegion2 merge(const TRegion2 rhs) const
	{
		return TRegion2(min.min(rhs.min), max.max(rhs.max));
	}
	TRegion2 intersect(const TRegion2 rhs) const
	{
		return TRegion2(min.max(rhs.min), max.min(rhs.max));
	}

	TRegion2 expand(const T rhs)
	{
		return TRegion2(cast(T)(min.x - rhs), cast(T)(min.y - rhs), cast(T)(max.x + rhs), cast(T)(max.y + rhs));
	}
	TRegion2 shrink(const T rhs)
	{
		return expand(-rhs);
	}

	TRegion2 dup() const { return TRegion2(min, max); }
	TRegion2 normal() const
	{
		return TRegion2(min.min(max), min.max(max));
	}

	TVector2!(T) center() const { return (min + max)/2; }
	alias center center;

	bool isValid() const { return min.x <= max.x  &&  min.y <= max.y; }

	bool overlaps(U)(const TRegion2!(U) rhs) const
	{
		return rhs.max.x > min.x  &&  rhs.max.y > min.y  &&  rhs.min.x < max.x  &&  rhs.min.y < max.y;
	}
	bool contains(U)(const TRegion2!(U) rhs) const
	{
		return rhs.min.x >= min.x  &&  rhs.min.y >= min.y  &&  rhs.max.x <= max.x  &&  rhs.max.y <= max.y;
	}
	bool contains(U)(const TVector2!(U) rhs) const
	{
		return rhs.x >= min.x  &&  rhs.y >= min.y  &&  rhs.x <= max.x  &&  rhs.y <= max.y;
	}

	TRegion2 cutBackX(T offset) const { return TRegion2(min, offset, max.y); }
	TRegion2 cutFrontX(T offset) const { return TRegion2(offset, min.y, max); }
	TRegion2 cutBackY(T offset) const { return TRegion2(min, max.x, offset); }
	TRegion2 cutFrontY(T offset) const { return TRegion2(min.x, offset, max); }

	T width() const { return cast(T)(max.x - min.x); }
	T width(const T rhs) { max.x = cast(T)(min.x + rhs); return rhs; }

	T height() const { return cast(T)(max.y - min.y); }
	T height(const T rhs) { max.y = cast(T)(min.y + rhs); return rhs; }

	TVector2!(T) dimensions() const { return max - min; }
	TVector2!(T) dimensions(TVector2!(T) rhs) { width(rhs.x); height(rhs.y); return rhs;}
}

alias TRegion2!(byte) Region2b;
alias TRegion2!(ubyte) Region2ub;
alias TRegion2!(short) Region2s;
alias TRegion2!(ushort) Region2us;
alias TRegion2!(int) Region2i;
alias TRegion2!(uint) Region2ui;
alias TRegion2!(long) Region2l;
alias TRegion2!(ulong) Region2ul;
alias TRegion2!(float) Region2f;
alias TRegion2!(real) Region2r;
alias TRegion2!(double) Region2d;
alias Region2i Region2;


struct TRegion3(T) {
	TVector3!(T) min, max;

	static TRegion3 opCall(const TVector3!(T) min, const TVector3!(T) max)
	{
		TRegion3 r;
		r.min = min;
		r.max = max;
		return r;
	}
	static TRegion3 opCall(T fx, T fy, T fz, const TVector3!(T) max)
	{
		TRegion3 r;
		r.min = TVector3!(T)(fx, fy, fz);
		r.max = max;
		return r;
	}
	static TRegion3 opCall(const TVector3!(T) min, T tx, T ty, T tz)
	{
		TRegion3 r;
		r.min = min;
		r.max = TVector3!(T)(tx, ty, tz);
		return r;
	}
	static TRegion3 opCall(T fx, T fy, T fz, T tx, T ty, T tz)
	{
		TRegion3 r;
		r.min = TVector3!(T)(fx, fy, fz);
		r.max = TVector3!(T)(tx, ty, tz);
		return r;
	}

	TRegion3 opAssign(const TRegion3 rhs) { min = rhs.min; max = rhs.max; return this; }

	TRegion3 merge(const TVector3!(T) rhs) const
	{
		return TRegion3(min.min(rhs), max.max(rhs));
	}
	TRegion3 merge(const TRegion3 rhs) const
	{
		return TRegion3(min.min(rhs.min), max.max(rhs.max));
	}
	TRegion3 intersect(const TRegion3 rhs) const
	{
		return TRegion3(min.max(rhs.min), max.min(rhs.max));
	}

	TRegion3 dup() const { return TRegion3(min, max); }
	TRegion3 normal() const
	{
		return TRegion3(min.min(max), min.max(max));
	}

	TVector3!(T) center() const { return (min + max)/2; }

	bool isValid() const { return min.x <= max.x  &&  min.y <= max.y  &&  min.z <= max.z; }

	bool overlaps(const TRegion3 rhs) const
	{
		return rhs.max.x > min.x  &&  rhs.max.y > min.y  &&  rhs.max.z > min.z
			&&  rhs.min.x < max.x  &&  rhs.min.y < max.y  &&  rhs.min.z < max.z
			;
	}
	bool contains(const TRegion3 rhs) const
	{
		return rhs.min.x >= min.x  &&  rhs.min.y >= min.y  &&  rhs.min.z >= min.z
			&&  rhs.max.x <= max.x  &&  rhs.max.y <= max.y  &&  rhs.max.z <= max.z
			;
	}

	TRegion3 cutBackX(T offset) const { return TRegion3(min, offset, max.y, max.z); }
	TRegion3 cutFrontX(T offset) const { return TRegion3(offset, min.y, min.z, max); }
	TRegion3 cutBackY(T offset) const { return TRegion3(min, max.x, offset, max.z); }
	TRegion3 cutFrontY(T offset) const { return TRegion3(min.x, offset, min.z, max); }
	TRegion3 cutBackZ(T offset) const { return TRegion3(min, max.x, max.y, offset); }
	TRegion3 cutFrontZ(T offset) const { return TRegion3(min.x, min.y, offset, max); }

	T width() const { return cast(T)(max.x - min.x); }
	T width(T rhs) { max.x = cast(T)(min.x + rhs); return rhs; }

	T height() const { return cast(T)(max.y - min.y); }
	T height(T rhs) { max.y = cast(T)(min.y + rhs); return rhs; }

	T depth() const { return cast(T)(max.z - min.z); }
	T depth(T rhs) { max.z = cast(T)(min.z + rhs); return rhs; }
}

alias TRegion3!(byte) Region3b;
alias TRegion3!(ubyte) Region3ub;
alias TRegion3!(short) Region3s;
alias TRegion3!(ushort) Region3us;
alias TRegion3!(int) Region3i;
alias TRegion3!(uint) Region3ui;
alias TRegion3!(long) Region3l;
alias TRegion3!(ulong) Region3ul;
alias TRegion3!(float) Region3f;
alias TRegion3!(real) Region3r;
alias TRegion3!(double) Region3d;
alias Region3i Region3;
